<!--
 * @Description: 微应用
 * @Author 卞鹏飞 <228443632@qq.com>
 * @create 27/09/24 PM11:20
 -->
<!--setup-->
<script setup lang="ts">
import { onMounted, getCurrentInstance, ref, type ComponentInternalInstance, shallowRef } from 'vue'
import { PlaceholderNetErrorDefault } from '@shared/svg'
import { loadMicroApp, singleSpa, type LoadableApp, setEnableCache } from 'qiankun'
import { loadJsSync, parseURL, to, uuid, noop } from 'sf-utils2'
import { shared } from './shared.ts'
import profile from '@root/profile.ts'
import { process } from '@/components/biz-micro-view/micro-app/css.ts'
import { QIANKUN_MICROAPP_WRAPPER_SELECTOR } from '@/components/biz-micro-view/micro-app/utils.ts'
import setting from '@/setting.ts'

defineOptions({
  name: 'MicroApp'
})

const { proxy } = getCurrentInstance() as ComponentInternalInstance

const props = defineProps({
  /**
   * 微应用名称
   */
  base: {
    type: String,
    default: () => ''
  },

  /**
   * 微应用的入口地址
   */
  entry: {
    type: String,
    default: () => ''
  },

  /**
   * 父应用传递给微应用的数据，详见
   */
  microAppProps: {
    type: Object,
    default: () => ({})
  },

  /**
   * 当前tabKey
   */
  tabKey: {
    type: String,
    default: ''
  }
})
const emit = defineEmits<{
  /**
   * 挂载成功
   * @param e
   * @param tabKey
   */
  (e: 'mounted-success', tabKey: string): void
}>()

/* 状态 */
setEnableCache(true) // 开发环境开启缓存
const MAX_TIME = 5 * 1000 // 最大加载子应用时间 8s
singleSpa.setUnmountMaxTime(MAX_TIME, true)
singleSpa.setMountMaxTime(MAX_TIME, true)

let uid = 0

const microApp = shallowRef()
const containerRef = ref<HTMLHtmlElement>()
const LOAD_STATUS_MAP = {
  NOT_LOADED: '加载失败，请重新刷新一下浏览器', // 加载失败
  LOADING_SOURCE_CODE: '正在加载中',
  NOT_BOOTSTRAPPED: '未绑定',
  BOOTSTRAPPING: '同步中',
  MOUNTING: '挂载中',
  MOUNTED: '已挂载',
  UPDATING: '更新中',
  UNMOUNTING: '取消挂载',
  UNLOADING: '卸载',
  NOT_MOUNTED: '加载失败，应用内部错误（挂载失败）', // 加载失败，应用内部错误（挂载失败）
  SKIP_BECAUSE_BROKEN: '加载失败，应用内部错误', // 加载失败，应用内部错误
  LOAD_ERROR: '加载失败',
  FAIL_FETCH_RESOURCE: '获取资源失败',
  NETWORK_ERROR: '网络错误', // 自定义
  NOT_FOUND_ENTRY: '，没有入口文件' // 自定义
}

const loading = ref(false)
const error = ref<{
  type?: keyof typeof LOAD_STATUS_MAP
  status?: boolean
  msg?: string
}>()

/* 方法 */

/**
 * 初始化
 */
const init = () => {
  mountedMicroApp()
}

/**
 * 加载子应用
 */
const mountedMicroApp = () => {
  if (!props.entry) {
    error.value = {
      status: true,
      type: 'NOT_FOUND_ENTRY',
      msg: LOAD_STATUS_MAP[`NOT_FOUND_ENTRY`]
    }
    return
  }
  loading.value = true

  const loadableAppInfo = {
    name: `${props.base}--` + uuid().slice(0, 6),
    entry: props.entry,
    container: containerRef.value,
    props: {
      id: ++uid,
      microId: props.tabKey, // 当前tabKey
      platform: setting.platform, // 微前端主应用
      container: containerRef.value,

      ...(props.microAppProps || {}),
      entry: props.entry,
      shared, // 分享数据【方便后期拓展】
      base: props.base // 基础路径
    }
  } as LoadableApp<any>

  // 非 hub主应用
  microApp.value = loadMicroApp(
    loadableAppInfo,
    {
      // autoStart: true,
      singular: false,
      sandbox: {
        speedy: true, // 是否开启高性能模式
        // strictStyleIsolation: false, // 开启 dom shadow
        experimentalStyleIsolation: true // 类似 vue scoped 作用域css
      },
      prefetch: false,

      /**
       * 排除哪些资源 经过qiankun
       * @param assetUrl
       */
      excludeAssetFilter: (assetUrl: string) => {
        if (!profile.IS_PRO) {
          if (assetUrl) {
            const reg = new RegExp(`^${window.location.origin}`)
            if (reg.test(assetUrl)) {
              // console.log('assetUrl', globalScriptCache, reg)
              return true
            }
          }
        }
        const urlObj = parseURL(assetUrl) as any
        // 有乾坤标识忽略
        return !!urlObj?.qiankunIgnore
      },

      /**
       * 获取入口文件地址
       * @param entry
       */
      getPublicPath: (entry: string) => {
        return entry
      },

      /**
       * 获取模版
       * @param tpl
       */
      getTemplate: (tpl: string) => tpl, // 获取模版

      /**
       * 加载js、css静态资源
       * 如果 js、css 地址 携带qiankunIgnore 且有as属性 为script 和 styleLink 则通过
       */
      fetch: {
        fn: async (url: string, ...args: any[]) => {
          // 判断是否是js 或者 css资源
          const isStyleOrScript = (url) => /^https?/.test(url) && /\.(css|js)/.test(url)

          const urlObj = parseURL(url) as Record<string, string>
          // https://front213.tezixing.com

          // 有乾坤标识忽略, 并且手动加上类型
          if (urlObj?.qiankunIgnore && isStyleOrScript(url) && ['script', 'styleLink'].includes(urlObj.as)) {
            await loadJsSync(url, noop)
            return Promise.resolve({
              status: 200,
              text: () => ''
            })
          }
          const [res, err] = await to(fetch(url, ...args))
          if (err) {
            shared.embedHTMLErrorCache[url] = true
            setTimeout(() => (loading.value = false), 16)
            if (err.toString().includes('Failed to fetch')) {
              error.value = {
                status: true,
                type: 'FAIL_FETCH_RESOURCE',
                msg: LOAD_STATUS_MAP['FAIL_FETCH_RESOURCE']
              }
            } else {
              error.value = {
                status: true,
                type: 'LOAD_ERROR',
                msg: LOAD_STATUS_MAP['LOAD_ERROR']
              }
            }
            return Promise.reject(err)
          }
          shared.embedHTMLErrorCache[url] && delete shared.embedHTMLErrorCache[url]
          shared.embedHTMLErrorCache[props.entry] && delete shared.embedHTMLErrorCache[props.entry]
          return res
        }
      },
      urlRerouteOnly: true // 只有url变化，才会触发popSate 和 hashchange
    },
    {
      beforeLoad: (): Promise<any> => undefined,
      beforeMount: async (): Promise<any> => {},
      afterMount: async (): Promise<any> => {
        emit('mounted-success', props.tabKey)
        loading.value = false

        // compact vite dev css
        if (!profile.IS_PRO) {
          // 开放环境
          const qiankunWrapperDom = containerRef.value.querySelector(
            `${QIANKUN_MICROAPP_WRAPPER_SELECTOR}`
          ) as HTMLDivElement
          if (qiankunWrapperDom) {
            const appName = qiankunWrapperDom?.dataset?.qiankun

            const qiankunHeadDom = containerRef.value.querySelector('qiankun-head')
            const qiankunHeadChildren = Array.from(qiankunHeadDom?.children) as unknown as HTMLHtmlElement[]
            const styleCacheMap = {} as Record<string, any>
            qiankunHeadChildren.forEach((item) => {
              console.log(' item.dataset.viteDevId',  item.tagName, item.dataset.viteDevId)
              if (item.tagName === 'STYLE' && (item.hasAttribute('data-vite-dev-id') || item.dataset.viteDevId)) {
                styleCacheMap[item.dataset.viteDevId] = true
              }
            })

            console.log('qiankunHeadDom', { qiankunHeadDom })

            const documentHeadChildren = [...document.head.children] as unknown as HTMLHtmlElement[]
            documentHeadChildren.forEach((item) => {
              if (item.tagName === 'STYLE' && (item.hasAttribute('data-vite-dev-id') || item.dataset.viteDevId)) {
                if (!styleCacheMap[item.dataset.viteDevId]) {
                  process(containerRef.value, item as unknown as HTMLLinkElement, appName)
                  qiankunHeadDom.appendChild(item)
                }
              }
            })
          }
        }

        // document.h
      },
      beforeUnmount: async (): Promise<any> => {},
      afterUnmount: async (): Promise<any> => {}
    }
  )
}

/**
 * 刷新一下
 */
const onRefresh = () => {
  init()
}

/* 计算 */

/* 监听 */

/* 周期 */
onMounted(() => {
  init()
})

/* 暴露 */
defineExpose({
  $: proxy.$
})
</script>

<!--render-->
<template>
  <div
    ref="scrollViewRef"
    v-spin="{
      loading,
      showLoadingText: true,
      loadingText: `微应用正在加载中... `,
      isFakeProgressText: true,
      mask: false,
      type: 'circle2'
    }"
    class="g-qiankun__micro-app"
  >
    <!-- 错误显示   -->
    <template v-if="error?.status">
      <div class="g-qiankun__container">
        <div class="g-qiankun__container-empty">
          <el-empty
            class="!bg-[#fff] !py-0"
            image-size="200"
          >
            <template #image>
              <PlaceholderNetErrorDefault />
            </template>
            <template #description>
              <div class="g-qiankun__container-empty-title mb-6px text-[14px] text-[#333]">
                页面加载失败
              </div>
              <div class="g-qiankun__container-empty-desc">
                请检查你的网络设置，或联系公司的技术人员（{{ error.type }}）
              </div>
            </template>
            <el-button
              type="primary"
              class="mb-50px"
              @click="onRefresh"
            >
              刷新一下
            </el-button>
          </el-empty>
        </div>
      </div>
    </template>

    <!-- 正确内容显示    -->
    <template v-else>
      <!--   是APP   -->
      <div
        ref="containerRef"
        class="g-qiankun__container"
      />
    </template>
  </div>
</template>

<!--style-->
<style scoped lang="less">
@scrollbar-width: 16px;
@nav-height: 36px;
@content-height: calc(100% - @nav-height);

//margin: 0 -16px;
.g-qiankun__micro-app {
  position: relative;
  height: 100%;
  padding: 0;
  background: #fff;

  .g-qiankun__container {
    width: 100%;
    height: 100%;

    & > div[data-qiankun] {
      //height: @content-height;
      & > div[id='app'] {
        //height: @content-height;
        background: #fff !important;
      }
    }
  }

  .g-qiankun__container-empty {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;

    .ijep-empty__description {
    }

    .g-qiankun__container-empty-title {
      margin-bottom: 10px;
      font-size: 14px;
      color: #666;
    }

    .g-qiankun__container-empty-desc {
      font-size: 13px;
      color: #999;
    }

    .ijep-empty__bottom {
      margin-top: 16px;
    }
  }
}
</style>
